$(function(){
	window.canvas = $('#turtle_canvas').turtle_canvas();
});

$.fn.extend({
	turtle_canvas: function(){
		var elem = this.get(0);
		if (elem.tagName.toLowerCase() == 'canvas'){
			return new TurtleCanvas(elem);
		}else{
			throw new Error('No canvas element found');
		}
	}
});

function TurtleCanvas(canvas_element){
	this.canvas = canvas_element.getContext('2d');
	this.width = parseInt(canvas_element.getAttribute('width'));
	this.height = parseInt(canvas_element.getAttribute('height'));
}
TurtleCanvas.prototype.eval_script = function(id){
    eval($(id)[0].value);	
};
TurtleCanvas.prototype.clear = function(){
	this.canvas.clearRect(0, 0, this.width, this.height);
};
TurtleCanvas.prototype.toString = function(){
	return '<TurtleCanvas/>';
}
TurtleCanvas.prototype.triangle = function(p1, p2, p3){
	this.canvas.beginPath();
	this.canvas.moveTo(p1.x, p1.y);
	this.canvas.lineTo(p2.x, p2.y);
	this.canvas.lineTo(p3.x, p3.y);
	this.canvas.closePath();
	this.canvas.fill();
};
TurtleCanvas.prototype.lines = function(paths){
	var self = this;
	$.each(paths, function(){
		if (this.length > 1){
			self.canvas.beginPath();
			self.canvas.moveTo(this[0].x, this[0].y);
			$.each(this.slice(1), function(){
				self.canvas.lineTo(this.x, this.y);
			});
		 	self.canvas.stroke();
		}
	});
};

$.extend({
	radians: function(degrees){
		return (Math.PI / 180.0) * degrees;
	}
});

function Scheduler(obj){
	this.schedule = []
	this.delay = 100;
	this.run_idx = 0;
	Scheduler.objects.push(obj);
	this.running = false;
}
Scheduler.objects = [];
Scheduler.partition = function(number, maxpart){
	var sign = 1;
	if (number < 0){
		sign = -1;
		number *= sign;
	}
	var segments = [];
	var whole_count = Math.floor(number / maxpart);
	var remainder = number % maxpart;
	for(var i = 0; i < whole_count; i++){
		segments.push(maxpart * sign);
	}
	if (remainder){
		segments.push(remainder * sign);
	}
	return segments;
};

Scheduler.prototype.clear = function(){
	this.schedule = [];
	this.running = false;
	this.run_idx = 0;
}
	
Scheduler.prototype.add = function(fn){
	this.schedule.push(fn);
	if (!this.running){ 
		this.run();
	}
};
	
Scheduler.prototype.run = function(){
	if(this.run_idx < this.schedule.length){
		this.running = true;
		canvas.clear();
		this.schedule[this.run_idx]();
		$.each(Scheduler.objects, function(idx, obj){
			obj.draw();
		});
		this.run_idx++;
		var self = this;
		setTimeout(function(){self.run();}, this.delay);
	}else{
		this.running = false;
	}
};

function Turtle(position, direction){
	this.position = position || {x: 100, y: 100};
	this.direction = $.radians(direction || 0);
	this.current_path = [this.position];
	this.paths = [this.current_path];
	this._pendown = true;
	this.visible = true;
	this.scheduler = new Scheduler(this);
}
Turtle.prototype._position_from = function(distance){
	var p = this.position;
	var d = this.direction;
	return {x: p.x + Math.cos(d) * distance, y: p.y + Math.sin(d) * distance};
}
Turtle.prototype.forward = function(distance){
	var self = this;
	var distance_partition = Scheduler.partition(distance, 10);
	$.each(distance_partition, function(idx, distance){
		self.scheduler.add(function(){
			self.position = self._position_from(distance);
			if (self._pendown){
				self.current_path.push(self.position);
			}
		});
	});
};
Turtle.prototype.right = function(degrees){
	this.left(-degrees);
};
Turtle.prototype.left = function(degrees){
	var self = this;
	var degree_partition = Scheduler.partition(degrees, 10);
	$.each(degree_partition, function(idx, degrees){
		self.scheduler.add(function(){
			self.direction -= $.radians(degrees);
		});
	});
};
Turtle.prototype.draw = function(degrees){
	canvas.lines(this.paths);
	if (this.visible){
		this._draw_turtle();
	}
};
Turtle.prototype.toString = function(){
	return 'Turtle facing ' + this.direction + ', at ' + this.position.x + ', ' + this.position.y;
};
Turtle.prototype._draw_turtle = function(){
	var p = this.position;
	var d = this.direction;
	var p1 = {x: p.x + Math.cos(d + $.radians(90)) * 5, y: p.y + Math.sin(d + $.radians(90)) * 5};
	var p2 = {x: p.x + Math.cos(d - $.radians(90)) * 5, y: p.y + Math.sin(d - $.radians(90)) * 5};
	var p3 = {x: p.x + Math.cos(d) * 8, y: p.y + Math.sin(d) * 8};
	canvas.triangle(p1, p2, p3);
};
Turtle.prototype.penup = function(){
	var self = this;
	this.scheduler.add(function(){
		self._pendown = false;
	});
};
Turtle.prototype.pendown = function(){
	var self = this;
	this.scheduler.add(function(){
		if (!self._pendown){
			self.current_path = [self.position];
			self.paths.push(self.current_path);
		}
		self._pendown = true;
	});
}
Turtle.prototype.show = function(){
	var self = this;
	this.scheduler.add(function(){
		self.visible = true;
	});
}
Turtle.prototype.hide = function(){
	var self = this;
	this.scheduler.add(function(){
		self.visible = false;
	});
}
Turtle.prototype.clear = function(){
	this.current_path = [this.position];
	this.paths = [this.current_path];
	this.scheduler.clear();
	canvas.clear();
	this.draw();
}
